package com.atguigu.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 在切面中需要通过指定的注解，将方法标识为通知方法
 * @before：前置通知：在目标方法执行之前
 * @after：后置通知，在目标方法的finally子句中执行的，返回异常通知
 * @AfterReturning：返回通知，在目标对象方法返回值之后，
 * @AfterThrowing:异常通知，在目标对象方法的catch子句中执行
 *
 *
 * 切入点表达式：设置在标识通知的注解的value属性中
 * execution(* com.atguigu.spring.aop.CalculatorImpl.*(..))")
 * 第一个*表示任意访问的修饰符和返回值类型
 * 第二个*表示类中的所有方法；
 * ..表示任意方法中的参数列表
 * 类的地方也可以使用*，就表示包下的所有的类
 * 3、重用切入点表达式
 * @PointCut就是声明一个公共的切入点表达式
 *      @Pointcut("execution(* com.atguigu.spring.aop.CalculatorImpl.*(..))")
 *     public void pointCut(){}//方法名
 *
 *    使用方式：：：@After("pointCut()")
 * 4、获取连接点的信息
 *      在通知方法的参数位置，设置JoinPoint类型的参数信息，就可以获取连接点所对应的方法的信息，名字，参数等
 * 5、切面的优先级
 *    可以通过@Order注解的value属性设置切面优先级，
 */
@Component
@Aspect//将当前组件表示为‘切面’
public class LoggerAspect {
    //切入点表达式的重用，
    @Pointcut("execution(* com.atguigu.spring.aop.CalculatorImpl.*(..))")
    public void pointCut(){}


    @Before("execution(* com.atguigu.spring.aop.CalculatorImpl.*(..))")//value是切入点的表达式,作用的方法
    public void beforeAdviceMethod(JoinPoint joinPoint){
        //获取连接点所对应的方法的签名信息(方法的)
        Signature signature = joinPoint.getSignature();
        //获取连接点所对应的方法中的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("LoggerAspect,前置通知");
        //System.out.println("LoggerAspect:方法"+signature.getName()+",参数："+ Arrays.toString(args));
    }

    //调用切入点表达式重用的；；；
    //程序结束时使用，finally最终都会执行
    @After("pointCut()")
    public void afterAdviceMethod(){
        System.out.println("LoggerAspect,后置通知()");
    }

    //没有异常的时候才会执行,returning用来接受目标对象方法的返回值的方法，
    //就可以将通知方法的某个参数指定为接受目标对象方法的返回值参数；
    @AfterReturning(value = "pointCut()" , returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint,Object result){
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect,方法名:"+signature.getName()+",结果"+result);
    }

    /**
     * 获取异常信息，在异常信息中要想获取以异常信息；
     * 就得通过AfterThrowing注解的throwing属性
     * 就可以将通知方法的某个参数指定为接受目标对象方法出现异常的参数
     */
    @AfterThrowing(value = "pointCut()",throwing = "ex")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint,Throwable ex){
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect，方法"+signature.getName()+",异常："+ex);
    }

    @Around("pointCut()")
    //环绕通知的方法的返回值一定要与目标对象的返回值一致
    public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint){
        Object proceed = null;
        try {
            System.out.println("环绕通知，前置通知-->");
            //可执行连接点的信息；表示目标对象执行的方法
            proceed = joinPoint.proceed();
            System.out.println("环绕通知，返回通知-->");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("环绕通知，异常通知-->");
        }finally {
            System.out.println("环绕通知，后置通知");
        }
        return proceed;
    }
}
